#ifndef __BLOCKINGQUEUE_HPP__
#define __BLOCKINGQUEUE_HPP__

#include <iostream>
#include <pthread.h>
#include <unistd.h>
#include <cstdint>
#include <queue>

#define CAPDEFAULT 5
template<class T>
class BlockQueue
{
public:
    BlockQueue(uint32_t cap = CAPDEFAULT)
        :_cap(cap)
        ,_p_wait_num(0)
        ,_c_wait_num(0)
    {
        pthread_mutex_init(&_lock, nullptr);
        pthread_cond_init(&_p_cond, nullptr);
        pthread_cond_init(&_c_cond, nullptr);
    }

    bool isFull()
    {
        if(_bq.size() >= _cap)
        {
            return true;
        }
        return false;
    }

    bool isEmpty()
    {
        if(_bq.size() <= 0)
            return true;
        return false;
    }

    void Enqueue(const T& in)
    {
        pthread_mutex_lock(&_lock);
        while(isFull())
        {
            ++_p_wait_num;
            pthread_cond_wait(&_p_cond, &_lock);
            //循环条件要用while，因为有可能出现伪唤醒情况，也就是本来唤醒了1号线程，1号线程去竞争锁，结果存在一个2号线程（可能没有
            //成功wait或者被虚假唤醒）抢先获得了锁，并且这个线程的任务也是生产一个数据（此时正好队列就差一个数据就满了，而2号进程
            //正好完成了这个任务），任务完成后2号线程把锁给了1号线程，此时如果判断条件是if，那么1号进程会认为我被唤醒并拿到了锁就
            //代表队列不满了，我可以向下进行插入数据了，结果就会出现越界的情况。判断条件改为while，就会再次进行判断队列是否满了

            //至于wait函数调用失败的的问题，解决方法就是后面加个判断条件解决
            --_p_wait_num;
        }
        _bq.push(in);

        //唤醒消费者进程
        if(_c_wait_num > 0)
        {
            pthread_cond_signal(&_c_cond);
        }
        pthread_mutex_unlock(&_lock);
    }

    void Pop(T* out)
    {
        pthread_mutex_lock(&_lock);

        while(isEmpty())
        {
            ++_c_wait_num;
            pthread_cond_wait(&_c_cond, &_lock);
            --_c_wait_num;
        }
        *out = _bq.front();
        _bq.pop();
        if(_p_wait_num > 0)
        {
            pthread_cond_signal(&_p_cond);
        }

        pthread_mutex_unlock(&_lock);
    }

    ~BlockQueue()
    {
        pthread_mutex_destroy(&_lock);
        pthread_cond_destroy(&_p_cond);
        pthread_cond_destroy(&_c_cond);
    }

private:
    std::queue<T> _bq;
    uint32_t _cap;//容量
    pthread_mutex_t _lock;
    pthread_cond_t _p_cond;//生产者所用条件变量
    pthread_cond_t _c_cond;//消费者所用条件变量

    int _p_wait_num;
    int _c_wait_num;
};


#endif